package agile.tools.autotest.persister.impl;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;

import agile.tools.autotest.MethodRecord;
import agile.tools.autotest.PropertyHolder;
import agile.tools.autotest.persister.Persister;

public class LocalFileSystemPersister implements Persister {
	static int filecount = 1;
	public void persist(MethodRecord record) {
		try {
			List<String> inputParamFileNames = saveInputParametersFrom(record);
			String returnValueFileName = saveReturnParameterFrom(record);
			FileUtils.writeStringToFile(new File(testFileLocationAndName(record)), objectToPersist(record, inputParamFileNames, returnValueFileName).toString());
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}
	
	private List<String> saveInputParametersFrom(MethodRecord record) {
		List<String> fileNames = new ArrayList<String>();
		for(int i = 0 ; i < record.serializedInputParams().size(); i++) {
			String serializedParam = record.serializedInputParams().get(i);
			String paramType = record.inputParamTypes().get(i);
			fileNames.add(write(serializedParam, paramType));
		}
		return fileNames;
	}
	
	private String saveReturnParameterFrom(MethodRecord record) {
		String serializedParam = record.serializedReturnValue().toString();
		String paramType = record.returnValueType();
		return write(serializedParam, paramType);
	}
	
	private String write(String serializedParam, String paramType) {
		try {
			String fileName = getFileName(paramType);
			FileUtils.writeStringToFile(new File(PropertyHolder.testsFolderLocation + fileName), serializedParam);
			return fileName;
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		return null;
	}
	
	private String getFileName(String paramType) {
		return "DATA-" + paramType + filecount++;
	}

	public List<Object[]> getAllTestDataSet() {
		List<Object[]> files = new ArrayList<Object[]>();
		File dir = new File(PropertyHolder.testsFolderLocation);
		FilenameFilter filter = new FilenameFilter() {
			public boolean accept(File dir, String name) {
				if(name.startsWith("TEST-"))
					return true;
				return false;
			}
		};
		String[] children = dir.list(filter);
		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				files.add(new Object[] { children[i], null });
			}
		}
		return files;
	}

	public MethodRecord getSingleTestDataSet(Object forDataSetReference) {
		MethodRecord methodRecord = new MethodRecord();
		try {
			String fileContents = FileUtils.readFileToString(new File(PropertyHolder.testsFolderLocation + forDataSetReference));
			getObjectUnderTest(methodRecord, fileContents);
			getMethodUnderTest(methodRecord, fileContents);
			getSerializedInputParams(methodRecord, fileContents);
			getSerializedReturnParam(methodRecord, fileContents);
			getTestRunner(methodRecord, fileContents);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return methodRecord;
	}
	
	private void getObjectUnderTest(MethodRecord methodRecord, String fileContents) {
		methodRecord.objectUnderTest(StringUtils.substringBetween(fileContents, "OBJECTUNDERTEST", "METHODNAME"));
	}
	
	private void getMethodUnderTest(MethodRecord methodRecord, String fileContents) {
		String methodNameIfInputsPresent = StringUtils.substringBetween(fileContents, "METHODNAME", "INPUTPARAM");
		if(! StringUtils.isBlank(methodNameIfInputsPresent)) {
			methodRecord.methodName(methodNameIfInputsPresent);
		} else {
			String methodNameIfNoInputsPresent = StringUtils.substringBetween(fileContents, "METHODNAME", "OUTPUTPARAM");
			methodRecord.methodName(methodNameIfNoInputsPresent);
		}
	}
	
	private void getSerializedInputParams(MethodRecord methodRecord, String fileContents) throws IOException {
		String allInputParamFileNames = StringUtils.substringBetween(fileContents, methodRecord.methodName(), "OUTPUTPARAM");
		String inputParamFileNames[] = StringUtils.splitByWholeSeparator(StringUtils.strip(allInputParamFileNames), "INPUTPARAM");
		if (inputParamFileNames != null) {
			for (String inputParamFileName : inputParamFileNames) {
				inputParamFileName = StringUtils.strip(inputParamFileName);
				methodRecord.serializedInputParam(FileUtils.readFileToString(new File(PropertyHolder.testsFolderLocation + inputParamFileName)));
			}
		}
	}
	
	private void getSerializedReturnParam(MethodRecord methodRecord, String fileContents) throws IOException {
		String fileName = StringUtils.strip(StringUtils.substringBetween(fileContents, "OUTPUTPARAM", "RUNNER"));
		methodRecord.serializedReturnValue(FileUtils.readFileToString(new File(PropertyHolder.testsFolderLocation + fileName)));
	}
	
	private void getTestRunner(MethodRecord methodRecord, String fileContents) {
		String runner = StringUtils.strip(StringUtils.substringAfter(fileContents, "RUNNER"));
		methodRecord.runner(runner);
	}
	
	private Object objectToPersist(MethodRecord record, List<String> inputParamFileNames, String returnValueFileName) {
		StringBuffer sb = new StringBuffer();
		sb.append("OBJECTUNDERTEST\n");
		sb.append(record.objectUnderTest());
		sb.append("\nMETHODNAME\n");
		sb.append(record.methodName());
		for (String pa : inputParamFileNames) {
			sb.append("\nINPUTPARAM\n");
			sb.append(pa);
		}
		sb.append("\nOUTPUTPARAM\n");
		sb.append(returnValueFileName);
		sb.append("\nRUNNER\n");
		sb.append("agile.tools.autotest.runner.RecordedTestRunner");
		return sb.toString();
	}
	
	private String testFileLocationAndName(MethodRecord record) {
		return PropertyHolder.testsFolderLocation + "TEST-" + record.objectUnderTest() + "-" + record.methodName() + record.inputParamTypesAsString() + filecount++ + ".txt";
	}
}